package org.example.tool;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

/**
 * @Classname Optional使用
 * @Description https://www.cnblogs.com/zhangboyu/p/7580262.html
 * @Date 2021/1/29 11:26 上午
 * @Created by xiangjd
 */
@Slf4j
public class Test {

    @org.junit.Test(expected = NullPointerException.class)
    public void whenCreateOfEmptyOptional_thenNullPointerException() {
        User1 user = null;
        Optional<User1> opt = Optional.of(user);
    }

    @org.junit.Test(expected = NoSuchElementException.class)
    public void whenCreateEmptyOptional_thenNull() {
        Optional<User1> emptyOpt = Optional.empty();
        emptyOpt.get();
    }

    @org.junit.Test
    public void whenCreateOfNullableOptional_thenOk() {
        String name = "John";
//        String name = null;
        Optional<String> opt = Optional.ofNullable(name);
        assertEquals("John", opt.get());
    }

    /**
     * ifPresent检查是否有值
     */
    @org.junit.Test
    public void whenCheckIfPresent_thenOk() {
        User1 user = new User1("john@gmail.com", "1234");
        Optional<User1> opt = Optional.ofNullable(user);
//        assertTrue(opt.isPresent());
//
//        assertEquals(user.getEmail(), opt.get().getEmail());
        opt.ifPresent(u -> assertEquals(user.getEmail(), u.getEmail()));
    }

    /**
     * orElse判断为空时给予默认值
     */
    @org.junit.Test
    public void whenEmptyValue_thenReturnDefault() {
        User1 user = null;
        User1 user2 = new User1("anna@gmail.com", "1234");
        User1 result = Optional.ofNullable(user).orElse(user2);
        assertEquals(user2.getEmail(), result.getEmail());
    }

    /**
     * 不为空的时候
     */
    @org.junit.Test
    public void whenValueNotNull_thenIgnoreDefault() {
        User1 user = new User1("john@gmail.com", "1234");
        User1 user2 = new User1("anna@gmail.com", "1234");
        // User1 result = Optional.ofNullable(user).orElse(user2);
        User1 result = Optional.ofNullable(user).orElseGet(() -> user2);
        assertEquals("john@gmail.com", result.getEmail());
    }

    /**
     * 为空的话orElse和orElseGet都要走对应的方法
     */
    @org.junit.Test
    public void givenEmptyValue_whenCompare_thenOk() {
        User1 user = null;
        log.debug("Using orElse");
        User1 result = Optional.ofNullable(user).orElse(createNewUser());
        log.debug("Using orElseGet");
        User1 result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
    }

    /**
     * 不为null的话orElse还是要走,但orElseGet不会走了
     */
    @org.junit.Test
    public void givenPresentValue_whenCompare_thenOk() {
        User1 user = new User1("john@gmail.com", "1234");
        log.info("Using orElse");
        User1 result = Optional.ofNullable(user).orElse(createNewUser());
        log.info("Using orElseGet");
        User1 result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
    }

    //这个示例中，两个 Optional  对象都包含非空值，两个方法都会返回对应的非空值。
// 不过，orElse() 方法仍然创建了 User 对象。与之相反，orElseGet() 方法不创建 User 对象。
//在执行较密集的调用时，比如调用 Web 服务或数据查询，这个差异会对性能产生重大影响。
    private User1 createNewUser() {
        log.debug("Creating New User");
        return new User1("extra@gmail.com", "1234");
    }

    /**
     * 自定义抛出异常
     */
    @org.junit.Test(expected = IllegalArgumentException.class)
    public void whenThrowException_thenOk() {
        User user = null;
        User result = Optional.ofNullable(user)
                .orElseThrow(() -> new IllegalArgumentException());
    }

    /**
     * 转换值
     */
    @org.junit.Test
    public void whenMap_thenOk() {
        User1 user = new User1("anna@gmail.com", "1234");
        String email = Optional.ofNullable(user)
                .map(u -> u.getEmail()).orElse("default@gmail.com");

        assertEquals(email, user.getEmail());
    }


    /**
     * flatMap() 也需要函数作为参数，并对值调用这个函数，然后直接返回结果。
     * <p>
     * 下面的操作中，我们给 User 类添加了一个方法，用来返回 Optional：
     * * 下面的操作中，我们给 User 类添加了一个方法，用来返回 Optional：
     */
    @org.junit.Test
    public void whenFlatMap_thenOk() {
        User1 user = new User1("anna@gmail.com", "1234");
        user.setPosition("Developer");
        String position = Optional.ofNullable(user)
                .flatMap(u -> u.getPosition()).orElse("default");

        assertEquals(position, user.getPosition().get());
    }


    /**
     * 过滤
     */
    @org.junit.Test
    public void whenFilter_thenOk() {
        User1 user = new User1("anna@gmail.com", "1234");
        Optional<User1> result = Optional.ofNullable(user)
                .filter(u -> u.getEmail() != null && u.getEmail().contains("@"));

        assertTrue(result.isPresent());
    }


    @org.junit.Test
    public void whenChaining_thenOk() {
        User1 user = new User1("anna@gmail.com", "1234");

//        String result = Optional.ofNullable(user)
//                .flatMap(u -> u.getAddress())
//                .flatMap(a -> a.getCountry())
//                .map(c -> c.getIsocode())
//                .orElse("default");

        String result = Optional.ofNullable(user)
                .flatMap(User1::getAddress)
                .flatMap(Address::getCountry)
                .map(Country::getIsocode)
                .orElse("default");
//        User2 user = new User2("anna@gmail.com", "1234");

//        String result = Optional.ofNullable(user)
//                .map(user2 -> user2.getAddress())
//                .map(address -> address.getCountry())
//                .map(country -> country.getIsocode())
//                .orElse("default");

        assertEquals(result, "default");
    }


    /**
     * Optional结合流式stream
     */
    @org.junit.Test
    public void whenEmptyStream_thenReturnDefaultOptional() {
        List<User1> users = new ArrayList<>();
        User1 user = users.stream().findFirst().orElse(new User1("default", "1234"));

        assertEquals(user.getEmail(), "default");
    }
}
